package com.kelp.codetoolkit.handler

import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiSearchHelper
import com.intellij.psi.search.UsageSearchContext
import com.intellij.psi.util.PsiTreeUtil
import com.kelp.codetoolkit.entity.CallerPosition
import com.kelp.codetoolkit.entity.GoBackPsiElement
import com.kelp.codetoolkit.respository.CallerHistoryManager

/**
 * @author: liuyuebai
 * @date: 2025/8/10 11:50
 * @description:
 */
class MethodNavigationHandler: GotoDeclarationHandler {
    /**
     * 处理Ctrl+鼠标点击事件，实现三大核心功能：
     * 1. 从方法调用处跳转到实现并记录调用点
     * 2. 从方法实现处显示调用方弹窗并添加返回选项
     * 3. 点击返回选项回到最近调用点
     */
    override fun getGotoDeclarationTargets(
        sourceElement: PsiElement?,
        offset: Int,
        editor: Editor?
    ): Array<out PsiElement?>? {
        if (sourceElement == null || editor == null) return null
        val project = sourceElement.project

        // 场景1：点击弹窗中的"返回调用点"选项（通过特殊注释识别）
        val backOption = isBackOption(sourceElement)
        if (backOption) {
            handleBackNavigation(sourceElement, project)
            return emptyArray()
        }

        // 场景2：点击方法调用（如 signBankService.saveBank(...)）
        val methodCall = PsiTreeUtil.getParentOfType(sourceElement, PsiMethodCallExpression::class.java)
        if (methodCall != null) {
            return handleMethodCall(methodCall, editor)
        }

        // 场景3：点击方法实现（如 SignBankService 中的 saveBank 方法）
        val methodImpl = PsiTreeUtil.getParentOfType(sourceElement, PsiMethod::class.java)
        if (methodImpl != null) {
            return handleMethodImplementation(methodImpl, project)
        }

        return null
    }

    /**
     * 判断元素是否为"返回调用点"选项（使用原生注释作为标记）
     */
    private fun isBackOption(element: PsiElement): Boolean {
        // 直接判断元素是否为自定义的GoBackPsiElement类型
        val bool = element is GoBackPsiElement
        return bool
    }

    /**
     * 处理返回调用点的导航逻辑
     */
    private fun handleBackNavigation(backOption: PsiElement, project: Project) {
        // 从返回选项关联到目标方法
        val targetMethod = PsiTreeUtil.getPrevSiblingOfType(backOption, PsiMethod::class.java) ?: return
        val callerPos = CallerHistoryManager.getLastCaller(targetMethod) ?: return

        // 跳转到记录的调用点位置
        FileEditorManager.getInstance(project).run {
            openFile(callerPos.file, true)
            selectedTextEditor?.caretModel?.moveToOffset(callerPos.offset)
        }
    }

    /**
     * 处理方法调用处的点击：记录调用点并返回方法实现
     */
    private fun handleMethodCall(methodCall: PsiMethodCallExpression, editor: Editor): Array<PsiElement?> {
        val targetMethod = methodCall.resolveMethod() ?: return emptyArray()

        // 记录当前调用点（如 SignBankHandleService 第108行）
        CallerHistoryManager.recordCaller(
            targetMethod,
            CallerPosition(
                file = editor.virtualFile,
                offset = editor.caretModel.offset,
                editor = editor
            )
        )

        return arrayOf(targetMethod) // 跳转到方法实现
    }

    /**
     * 处理方法实现处的点击：返回调用方列表+返回选项
     */
    private fun handleMethodImplementation(method: PsiMethod, project: Project): Array<PsiElement> {
        // 查找所有调用方
        val callers = findMethodCallers(method, project)
        val popupItems = mutableListOf<PsiElement>()
        popupItems.addAll(callers)

        // 添加"返回调用点"选项（使用原生注释元素）
        if (popupItems.size > 1 && CallerHistoryManager.getLastCaller(method) != null) {
            val backOption = GoBackPsiElement(PsiManager.getInstance(project),project,method)
            popupItems.add(0,backOption)
        }
        return popupItems.distinct().toTypedArray() // 原生弹窗会显示这些选项
    }

    /**
     * 查找方法的所有调用方（适配 UsageSearchContext.IN_CODE 为 Short 类型）
     */
    private fun findMethodCallers(method: PsiMethod, project: Project): List<PsiElement> {
        val callers = mutableListOf<PsiElement>()
        val searchScope = GlobalSearchScope.allScope(project)
        val searchHelper = PsiSearchHelper.getInstance(project)

        // 使用ProgressManager包裹耗时的搜索操作，提供进度指示器上下文
        ProgressManager.getInstance().runProcess({
            searchHelper.processElementsWithWord(
                { element, _ ->
                    // 检查进度是否被取消（用户点击取消按钮时）
                    if (ProgressManager.getInstance().progressIndicator.isCanceled) {
                        return@processElementsWithWord false // 停止搜索
                    }

                    val methodCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression::class.java)
                    if (methodCall != null && methodCall.resolveMethod() == method) {
                        callers.add(methodCall)
                    }
                    true // 继续搜索
                },
                searchScope,
                method.name,
                UsageSearchContext.IN_CODE,
                true
            )
        }, EmptyProgressIndicator()) // 提供进度指示器实例

        return callers
    }
}